﻿using SortingNetworkSimulator.Model;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SortingNetworkSimulator.View
{
    public class NetworkDesigner
    {
        private const int WiresX1 = 10;
        private const int WiresX2 = 500;
        private const int WiresInitialY = 30;
        private const int WiresSpaceSize = 50;
        private const float WireWidth = 2.0f;
        private const int LayerInitialX = 60;
        private const int LayerSpaceSize = 40;
        private const float CircleDiameter = 10.0f;
        private const int ValueBoxX = 10;
        private const int ResultBoxX = 610;
        private const int LayerSeparatorEndingHeight = 20;
        private const int LayerSeparatorShift = 20;
        private const int LayerSeperatorDotHeight = 20;
        private const int LayerSeperatorSpaceHeight = 10;
        private const int LayerSeparatorInitialY = 5;
        private const float LayerSeparatorWidth = 1.5f;

        private Graphics networkGraphics;
        private Bitmap networkImage;
        private PictureBox networkContainer;
        private Panel networkPanel;

        public NetworkDesigner(PictureBox networkContainer, Panel networkPanel, Network network)
        {
            this.networkContainer = networkContainer;
            this.networkPanel = networkPanel;

            int height, width;

            if (network.InputsCount * WiresSpaceSize < networkContainer.Height)
                height = networkContainer.Height;
            else
                height = network.InputsCount * WiresSpaceSize;

            int tempWidth = 0;
            foreach (var layerWidth in network.LayersDepths)
            {
                tempWidth += layerWidth.Value;
            }

            if (network.Comparators.Count > 0 && tempWidth > 10)
                width = 130 + tempWidth * 40;
            else
                width = 560;

            this.networkImage = new Bitmap(width, height);
            
            this.networkGraphics = Graphics.FromImage(networkImage);
        }


        public void DrawNetwork(Network network, bool showLayers)
        {
            networkGraphics.Clear(Color.White);
            int tempWidth = 0;
            foreach (var layerWidth in network.LayersDepths)
            {
                tempWidth += layerWidth.Value;
            }

            if (network.Comparators.Count > 0 && tempWidth > 10)
            {
                DrawWires(network.InputsCount, 70 + tempWidth * 40);
            }
            else
            {
                DrawWires(network.InputsCount);
            }
            DrawComparators(network);

            if (showLayers)
            {
                DrawLayersSeparators(network);
            }
            
            networkContainer.Image = networkImage;
        }

        public void DrawValueBoxes(int p)
        {
            TextBox valueBox;
            for (int i = 0; i < p; i++)
            {
                int valueBoxY = GetWireY(i) - 10;
                valueBox = new TextBox();
                valueBox.Name = "valueBox" + i.ToString();
                valueBox.Height = 20;
                valueBox.Width = 20;
                valueBox.Location = new Point(ValueBoxX, valueBoxY);

                networkPanel.Controls.Add(valueBox);
            }
        }

        public void DrawResultBoxes(int p)
        {
            TextBox resultBox;
            for (int i = 0; i < p; i++)
            {
                int resultBoxY = GetWireY(i) - 10;
                resultBox = new TextBox();
                resultBox.Name = "resultBox" + i.ToString();
                resultBox.Height = 20;
                resultBox.Width = 20;
                resultBox.Location = new Point(networkContainer.Width-30, resultBoxY);
                resultBox.Enabled = false;

                networkContainer.Controls.Add(resultBox);
            }
        }

        private void DrawWires(int inputsCount, int layers = 0)
        {
            using (Pen pen = new Pen(Color.Black, WireWidth))
            {
                for (int i = 0; i < inputsCount; i++)
                {
                    int wireY = GetWireY(i);
                    if (layers != 0)
                        networkGraphics.DrawLine(pen, WiresX1, wireY, layers, wireY);  
                    else
                        networkGraphics.DrawLine(pen, WiresX1, wireY, WiresX2, wireY); 

                    using (Font font = new Font("Arial", 10.0f, GraphicsUnit.Pixel))
                    {
                        networkGraphics.DrawString(i.ToString(), font, Brushes.Black, WiresX1 - 10, wireY - 7); 
                    }
                }
            }

            
        }

        private void DrawComparators(Network network)
        {
            SolidBrush brush = new SolidBrush(Color.Black);

            int currentLayer = 0;
            int currentDepth = 0;

            foreach (Comparator comparator in network.Comparators.OrderBy(c => c.LayerIndex))
            {
                if (comparator.LayerIndex > currentLayer)
                {
                    currentDepth += network.LayersDepths[currentLayer];
                    currentLayer++;
                }

                int wireY1 = GetWireY(comparator.WireOneIndex);
                int wireY2 = GetWireY(comparator.WireTwoIndex);
                int wireX = LayerInitialX + currentDepth * LayerSpaceSize + comparator.DrawingLayerIndex * LayerSpaceSize;

                float circleRadius = CircleDiameter / 2;
                int upperCircleY = (int)(wireY1 - circleRadius);
                int lowerTwoCircleY = (int)(wireY2 - circleRadius);
                int circleX = (int)(wireX - circleRadius);

                networkGraphics.FillEllipse(brush, circleX, upperCircleY, CircleDiameter, CircleDiameter);
                networkGraphics.FillEllipse(brush, circleX, lowerTwoCircleY, CircleDiameter, CircleDiameter);

                using (Pen pen = new Pen(Color.Black, WireWidth))
                {
                    networkGraphics.DrawLine(pen, wireX, wireY1, wireX, wireY2);
                }
            }
        }

        private int GetWireY(int wireIndex)
        {
            return WiresInitialY + wireIndex * WiresSpaceSize;
        }

        private void DrawLayersSeparators(Network network)
        {
            int separatorHeight = (network.InputsCount - 1) * WiresSpaceSize + 2 * LayerSeparatorEndingHeight;
            int currentDepth = 0;

            foreach (var layerDepth in network.LayersDepths)
            {
                currentDepth += layerDepth.Value;

                int separatorX = LayerInitialX + (currentDepth - 1) * LayerSpaceSize + LayerSeparatorShift;

                int step = LayerSeperatorDotHeight + LayerSeperatorSpaceHeight;

                float labelX;

                if (layerDepth.Key > 9)
                {
                    labelX = separatorX - 20;
                }
                else
                {
                    labelX = separatorX - 15;
                }

                float labelY = LayerSeparatorInitialY;

                using (Font font = new Font("Arial", 10.0f, GraphicsUnit.Pixel))
                {
                    networkGraphics.DrawString(string.Format("L{0}", layerDepth.Key), font, Brushes.Black, labelX, labelY);
                }

                using (Pen pen = new Pen(Color.Black, LayerSeparatorWidth))
                {
                    for (int separatorY = LayerSeparatorInitialY; separatorY < separatorHeight; separatorY += step)
                    {
                        networkGraphics.DrawLine(pen, separatorX, separatorY, separatorX, separatorY + LayerSeperatorDotHeight);
                    }
                }
            }
        }

    }
}
